/*
* Copyright (C) 2020  钟先耀
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
*/
/*	$OpenBSD: ieee80211_pae_output.c,v 1.30 2017/12/21 12:09:38 mpi Exp $	*/

/*-
 * Copyright (c) 2007,2008 Damien Bergamini <damien.bergamini@free.fr>
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

/*
 * This code implements the 4-Way Handshake and Group Key Handshake protocols
 * (both Supplicant and Authenticator Key Transmit state machines) defined in
 * IEEE Std 802.11-2007 section 8.5.
 */

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/mbuf.h>
#include <sys/kernel.h>
#include <sys/socket.h>
#include <sys/sockio.h>
#include <sys/endian.h>
#include <sys/errno.h>

#include <net/if.h>
#include <net/if_dl.h>
#include <net/if_media.h>
#include <net/if_llc.h>

#include <netinet/in.h>
#include <netinet/if_ether.h>
#include <netinet/ip.h>

#include <net80211/ieee80211_var.h>
#include <net80211/ieee80211_priv.h>

int		ieee80211_send_eapol_key(struct ieee80211com *ic, mbuf_t m,
                                 struct ieee80211_node *ni, const struct ieee80211_ptk *ptk);
#ifndef IEEE80211_STA_ONLY
u_int8_t	*ieee80211_add_gtk_kde(u_int8_t *, struct ieee80211_node *,
                                   const struct ieee80211_key *);
u_int8_t	*ieee80211_add_pmkid_kde(u_int8_t *, const u_int8_t *);
u_int8_t	*ieee80211_add_igtk_kde(u_int8_t *,
                                    const struct ieee80211_key *);
#endif
mbuf_t 	ieee80211_get_eapol_key(int, int, u_int);

/*
 * Send an EAPOL-Key frame to node `ni'.  If MIC or encryption is required,
 * the PTK must be passed (otherwise it can be set to NULL.)
 */
int
ieee80211_send_eapol_key(struct ieee80211com *ic, mbuf_t m,
                         struct ieee80211_node *ni, const struct ieee80211_ptk *ptk)
{
    XYLog("%s\n", __FUNCTION__);
    struct ifnet *ifp = &ic->ic_if;
    struct ether_header *eh;
    struct ieee80211_eapol_key *key;
    u_int16_t info;
    int len, error = 0;
    
    mbuf_prepend(&m, sizeof(struct ether_header), MBUF_DONTWAIT);
    if (m == NULL)
        return ENOMEM;
    /* no need to m_pullup here (ok by construction) */
    eh = mtod(m, struct ether_header *);
    eh->ether_type = htons(ETHERTYPE_PAE);
    IEEE80211_ADDR_COPY(eh->ether_shost, ic->ic_myaddr);
    IEEE80211_ADDR_COPY(eh->ether_dhost, ni->ni_macaddr);
    
    key = (struct ieee80211_eapol_key *)&eh[1];
    key->version = EAPOL_VERSION;
    key->type = EAPOL_KEY;
    key->desc = (ni->ni_rsnprotos == IEEE80211_PROTO_RSN) ?
    EAPOL_KEY_DESC_IEEE80211 : EAPOL_KEY_DESC_WPA;
    
    info = BE_READ_2(key->info);
    /* use V3 descriptor if KDF is SHA256-based */
    if (ieee80211_is_sha256_akm((enum ieee80211_akm)ni->ni_rsnakms))
        info |= EAPOL_KEY_DESC_V3;
    /* use V2 descriptor if pairwise or group cipher is CCMP */
    else if (ni->ni_rsncipher == IEEE80211_CIPHER_CCMP ||
             ni->ni_rsngroupcipher == IEEE80211_CIPHER_CCMP)
        info |= EAPOL_KEY_DESC_V2;
    else
        info |= EAPOL_KEY_DESC_V1;
    BE_WRITE_2(key->info, info);
    
    len = mbuf_len(m) - sizeof(struct ether_header);
    BE_WRITE_2(key->paylen, len - sizeof(*key));
    BE_WRITE_2(key->len, len - 4);
    
#ifndef IEEE80211_STA_ONLY
    if (info & EAPOL_KEY_ENCRYPTED) {
        if (ni->ni_rsnprotos == IEEE80211_PROTO_WPA) {
            /* clear "Encrypted" bit for WPA */
            info &= ~EAPOL_KEY_ENCRYPTED;
            BE_WRITE_2(key->info, info);
        }
        ieee80211_eapol_key_encrypt(ic, key, ptk->kek);
        
        if ((info & EAPOL_KEY_VERSION_MASK) != EAPOL_KEY_DESC_V1) {
            /* AES Key Wrap adds 8 bytes + padding */
            size_t l = sizeof(*eh) + 4 + BE_READ_2(key->len);
            mbuf_pkthdr_setlen(m, l);
            mbuf_setlen(m, l);
        }
    }
#endif
    if (info & EAPOL_KEY_KEYMIC)
        ieee80211_eapol_key_mic(key, ptk->kck);
    
#ifndef IEEE80211_STA_ONLY
    /* start a 100ms timeout if an answer is expected from supplicant */
    if (info & EAPOL_KEY_KEYACK)
        timeout_add_msec(&ni->ni_eapol_to, 100);
#endif
    
    if (!ifp->if_snd->lockEnqueue(m)) {
        XYLog("%s 啊啊啊啊 enqueue fail!!\n", __FUNCTION__);
        return -1;
    }
    (*ifp->if_start)(ifp);
    return 0;
}

#ifndef IEEE80211_STA_ONLY
/*
 * Handle EAPOL-Key timeouts (no answer from supplicant).
 */
void
ieee80211_eapol_timeout(void *arg)
{
    struct ieee80211_node *ni = (struct ieee80211_node *)arg;
    struct ieee80211com *ic = ni->ni_ic;
    int s;
    
    DPRINTF(("no answer from station %s in state %d\n",
        ether_sprintf(ni->ni_macaddr), ni->ni_rsn_state));

    s = splnet();

    switch (ni->ni_rsn_state) {
    case RSNA_PTKSTART:
    case RSNA_PTKCALCNEGOTIATING:
        (void)ieee80211_send_4way_msg1(ic, ni);
        break;
    case RSNA_PTKINITNEGOTIATING:
        (void)ieee80211_send_4way_msg3(ic, ni);
        break;
    }

    switch (ni->ni_rsn_gstate) {
    case RSNA_REKEYNEGOTIATING:
        (void)ieee80211_send_group_msg1(ic, ni);
        break;
    }

    splx(s);
}

/*
 * Add a GTK KDE to an EAPOL-Key frame (see Figure 144).
 */
u_int8_t *
ieee80211_add_gtk_kde(u_int8_t *frm, struct ieee80211_node *ni,
                      const struct ieee80211_key *k)
{
    _KASSERT(k->k_flags & IEEE80211_KEY_GROUP);
    
    *frm++ = IEEE80211_ELEMID_VENDOR;
    *frm++ = 6 + k->k_len;
    memcpy(frm, IEEE80211_OUI, 3); frm += 3;
    *frm++ = IEEE80211_KDE_GTK;
    *frm = k->k_id & 3;
    /*
     * The TxRx flag for sending a GTK is always the opposite of whether
     * the pairwise key is used for data encryption/integrity or not.
     */
    if (ni->ni_rsncipher == IEEE80211_CIPHER_USEGROUP)
        *frm |= 1 << 2;    /* set the Tx bit */
    frm++;
    *frm++ = 0;    /* reserved */
    memcpy(frm, k->k_key, k->k_len);
    return frm + k->k_len;
}

/*
 * Add a PMKID KDE to an EAPOL-Key frame (see Figure 146).
 */
u_int8_t *
ieee80211_add_pmkid_kde(u_int8_t *frm, const u_int8_t *pmkid)
{
    *frm++ = IEEE80211_ELEMID_VENDOR;
    *frm++ = 20;
    memcpy(frm, IEEE80211_OUI, 3); frm += 3;
    *frm++ = IEEE80211_KDE_PMKID;
    memcpy(frm, pmkid, IEEE80211_PMKID_LEN);
    return frm + IEEE80211_PMKID_LEN;
}

/*
 * Add an IGTK KDE to an EAPOL-Key frame (see Figure 8-32a).
 */
u_int8_t *
ieee80211_add_igtk_kde(u_int8_t *frm, const struct ieee80211_key *k)
{
    _KASSERT(k->k_flags & IEEE80211_KEY_IGTK);
    
    *frm++ = IEEE80211_ELEMID_VENDOR;
    *frm++ = 4 + 24;
    memcpy(frm, IEEE80211_OUI, 3); frm += 3;
    *frm++ = IEEE80211_KDE_IGTK;
    LE_WRITE_2(frm, k->k_id); frm += 2;
    LE_WRITE_6(frm, k->k_tsc); frm += 6;    /* IPN */
    memcpy(frm, k->k_key, 16);
    return frm + 16;
}
#endif	/* IEEE80211_STA_ONLY */

mbuf_t
ieee80211_get_eapol_key(int flags, int type, u_int pktlen)
{
    mbuf_t m;
    
    /* reserve space for 802.11 encapsulation and EAPOL-Key header */
    pktlen += sizeof(struct ieee80211_frame) + LLC_SNAPFRAMELEN +
    sizeof(struct ieee80211_eapol_key);
    
    if (pktlen > MCLBYTES)
        panic("EAPOL-Key frame too large: %u", pktlen);
    mbuf_gethdr(flags, type, &m);
    if (m == NULL)
        return NULL;
    if (pktlen > mbuf_get_mhlen()) {
        mbuf_mclget(flags, type, &m);
        if (!(mbuf_flags(m) & MBUF_EXT))
            return mbuf_free(m);
    }
    mbuf_setdata(m, (char*)mbuf_data(m) +
                 sizeof(struct ieee80211_frame) +
                 LLC_SNAPFRAMELEN, mbuf_len(m)
                 - sizeof(struct ieee80211_frame) - LLC_SNAPFRAMELEN);
    return m;
}

#ifndef IEEE80211_STA_ONLY
/*
 * Send 4-Way Handshake Message 1 to the supplicant.
 */
int
ieee80211_send_4way_msg1(struct ieee80211com *ic, struct ieee80211_node *ni)
{
    XYLog("%s\n", __FUNCTION__);
    struct ieee80211_eapol_key *key;
    mbuf_t m;
    u_int16_t info, keylen;
    u_int8_t *frm;
    
    ni->ni_rsn_state = RSNA_PTKSTART;
    if (++ni->ni_rsn_retries > 3) {
        IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_DEAUTH,
                            IEEE80211_REASON_4WAY_TIMEOUT);
        ieee80211_node_leave(ic, ni);
        return 0;
    }
    m = ieee80211_get_eapol_key(MBUF_DONTWAIT, MT_DATA,
                                (ni->ni_rsnprotos == IEEE80211_PROTO_RSN) ? 2 + 20 : 0);
    if (m == NULL) {
        XYLog("%s\n ieee80211_get_eapol_key==NULL", __FUNCTION__);
        return ENOMEM;
    }
    key = mtod(m, struct ieee80211_eapol_key *);
    memset(key, 0, sizeof(*key));
    
    info = EAPOL_KEY_PAIRWISE | EAPOL_KEY_KEYACK;
    BE_WRITE_2(key->info, info);
    
    /* copy the authenticator's nonce (ANonce) */
    memcpy(key->nonce, ni->ni_nonce, EAPOL_KEY_NONCE_LEN);
    
    keylen = ieee80211_cipher_keylen(ni->ni_rsncipher);
    BE_WRITE_2(key->keylen, keylen);
    
    frm = (u_int8_t *)&key[1];
    /* NB: WPA does not have PMKID KDE */
    if (ni->ni_rsnprotos == IEEE80211_PROTO_RSN &&
        ieee80211_is_8021x_akm((enum ieee80211_akm)ni->ni_rsnakms))
        frm = ieee80211_add_pmkid_kde(frm, ni->ni_pmkid);
    
    size_t l = frm - (u_int8_t *)key;
    mbuf_pkthdr_setlen(m, l);
    mbuf_setlen(m, l);
    
    if (ic->ic_if.if_flags & IFF_DEBUG)
        XYLog("%s: sending msg %d/%d of the %s handshake to %s\n",
               ic->ic_if.if_xname, 1, 4, "4-way",
               ether_sprintf(ni->ni_macaddr));
    
    ni->ni_replaycnt++;
    BE_WRITE_8(key->replaycnt, ni->ni_replaycnt);
    
    return ieee80211_send_eapol_key(ic, m, ni, NULL);
}
#endif	/* IEEE80211_STA_ONLY */

/*
 * Send 4-Way Handshake Message 2 to the authenticator.
 */
int
ieee80211_send_4way_msg2(struct ieee80211com *ic, struct ieee80211_node *ni,
                         const u_int8_t *replaycnt, const struct ieee80211_ptk *tptk)
{
    XYLog("%s\n", __FUNCTION__);
    struct ieee80211_eapol_key *key;
    mbuf_t m;
    u_int16_t info;
    u_int8_t *frm;
    
    ni->ni_rsn_supp_state = RSNA_SUPP_PTKNEGOTIATING;
    m = ieee80211_get_eapol_key(MBUF_DONTWAIT, MT_DATA,
                                (ni->ni_rsnprotos == IEEE80211_PROTO_WPA) ?
                                2 + IEEE80211_WPAIE_MAXLEN :
                                2 + IEEE80211_RSNIE_MAXLEN);
    if (m == NULL)
        return ENOMEM;
    key = mtod(m, struct ieee80211_eapol_key *);
    memset(key, 0, sizeof(*key));
    
    info = EAPOL_KEY_PAIRWISE | EAPOL_KEY_KEYMIC;
    BE_WRITE_2(key->info, info);
    
    /* copy key replay counter from Message 1/4 */
    memcpy(key->replaycnt, replaycnt, 8);
    
    /* copy the supplicant's nonce (SNonce) */
    memcpy(key->nonce, ic->ic_nonce, EAPOL_KEY_NONCE_LEN);
    
    frm = (u_int8_t *)&key[1];
    /* add the WPA/RSN IE used in the (Re)Association Request */
    if (ni->ni_rsnprotos == IEEE80211_PROTO_WPA) {
        int keylen;
        frm = ieee80211_add_wpa(frm, ic, ni);
        /* WPA sets the key length field here */
        keylen = ieee80211_cipher_keylen(ni->ni_rsncipher);
        BE_WRITE_2(key->keylen, keylen);
    } else	/* RSN */
        frm = ieee80211_add_rsn(frm, ic, ni);
    size_t l = frm - (u_int8_t *)key;
    mbuf_pkthdr_setlen(m, l);
    mbuf_setlen(m, l);
    
    if (ic->ic_if.if_flags & IFF_DEBUG)
        XYLog("%s: sending msg %d/%d of the %s handshake to %s, type=%s\n",
               ic->ic_if.if_xname, 2, 4, "4-way",
              ether_sprintf(ni->ni_macaddr), ni->ni_rsnprotos == IEEE80211_PROTO_WPA ? "WPA" : "RSN");
    
    return ieee80211_send_eapol_key(ic, m, ni, tptk);
}

#ifndef IEEE80211_STA_ONLY
/*
 * Send 4-Way Handshake Message 3 to the supplicant.
 */
int
ieee80211_send_4way_msg3(struct ieee80211com *ic, struct ieee80211_node *ni)
{
    XYLog("%s\n", __FUNCTION__);
    struct ieee80211_eapol_key *key;
    struct ieee80211_key *k = NULL;
    mbuf_t m;
    u_int16_t info, keylen;
    u_int8_t *frm;
    
    ni->ni_rsn_state = RSNA_PTKINITNEGOTIATING;
    if (++ni->ni_rsn_retries > 3) {
        IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_DEAUTH,
                            IEEE80211_REASON_4WAY_TIMEOUT);
        ieee80211_node_leave(ic, ni);
        return 0;
    }
    if (ni->ni_rsnprotos == IEEE80211_PROTO_RSN) {
        k = &ic->ic_nw_keys[ic->ic_def_txkey];
        m = ieee80211_get_eapol_key(MBUF_DONTWAIT, MT_DATA,
                                    2 + IEEE80211_RSNIE_MAXLEN + 2 + 6 + k->k_len + 15 +
                                    ((ni->ni_flags & IEEE80211_NODE_MFP) ? 2 + 28 : 0));
    } else { /* WPA */
        m = ieee80211_get_eapol_key(MBUF_DONTWAIT, MT_DATA,
                                    2 + IEEE80211_WPAIE_MAXLEN +
                                    ((ni->ni_flags & IEEE80211_NODE_MFP) ? 2 + 28 : 0));
    }
    if (m == NULL)
        return ENOMEM;
    key = mtod(m, struct ieee80211_eapol_key *);
    memset(key, 0, sizeof(*key));
    
    info = EAPOL_KEY_PAIRWISE | EAPOL_KEY_KEYACK | EAPOL_KEY_KEYMIC;
    if (ni->ni_rsncipher != IEEE80211_CIPHER_USEGROUP)
        info |= EAPOL_KEY_INSTALL;
    
    /* use same nonce as in Message 1 */
    memcpy(key->nonce, ni->ni_nonce, EAPOL_KEY_NONCE_LEN);
    
    ni->ni_replaycnt++;
    BE_WRITE_8(key->replaycnt, ni->ni_replaycnt);
    
    keylen = ieee80211_cipher_keylen(ni->ni_rsncipher);
    BE_WRITE_2(key->keylen, keylen);
    
    frm = (u_int8_t *)&key[1];
    /* add the WPA/RSN IE included in Beacon/Probe Response */
    if (ni->ni_rsnprotos == IEEE80211_PROTO_RSN) {
        frm = ieee80211_add_rsn(frm, ic, ic->ic_bss);
        /* encapsulate the GTK */
        frm = ieee80211_add_gtk_kde(frm, ni, k);
        LE_WRITE_6(key->rsc, k->k_tsc);
        /* encapsulate the IGTK if MFP was negotiated */
        if (ni->ni_flags & IEEE80211_NODE_MFP) {
            frm = ieee80211_add_igtk_kde(frm,
                                         &ic->ic_nw_keys[ic->ic_igtk_kid]);
        }
        /* ask that the EAPOL-Key frame be encrypted */
        info |= EAPOL_KEY_ENCRYPTED | EAPOL_KEY_SECURE;
    } else	/* WPA */
        frm = ieee80211_add_wpa(frm, ic, ic->ic_bss);
    
    /* write the key info field */
    BE_WRITE_2(key->info, info);
    
    size_t l = frm - (u_int8_t *)key;
    mbuf_pkthdr_setlen(m, l);
    mbuf_setlen(m, l);
    
    if (ic->ic_if.if_flags & IFF_DEBUG)
        XYLog("%s: sending msg %d/%d of the %s handshake to %s\n",
               ic->ic_if.if_xname, 3, 4, "4-way",
               ether_sprintf(ni->ni_macaddr));
    
    return ieee80211_send_eapol_key(ic, m, ni, &ni->ni_ptk);
}
#endif	/* IEEE80211_STA_ONLY */

/*
 * Send 4-Way Handshake Message 4 to the authenticator.
 */
int
ieee80211_send_4way_msg4(struct ieee80211com *ic, struct ieee80211_node *ni)
{
    XYLog("%s\n", __FUNCTION__);
    struct ieee80211_eapol_key *key;
    mbuf_t m;
    u_int16_t info;
    
    ni->ni_rsn_supp_state = RNSA_SUPP_PTKDONE;
    m = ieee80211_get_eapol_key(MBUF_DONTWAIT, MT_DATA, 0);
    if (m == NULL)
        return ENOMEM;
    key = mtod(m, struct ieee80211_eapol_key *);
    memset(key, 0, sizeof(*key));
    
    info = EAPOL_KEY_PAIRWISE | EAPOL_KEY_KEYMIC;
    
    /* copy key replay counter from authenticator */
    BE_WRITE_8(key->replaycnt, ni->ni_replaycnt);
    
    if (ni->ni_rsnprotos == IEEE80211_PROTO_WPA) {
        int keylen;
        /* WPA sets the key length field here */
        keylen = ieee80211_cipher_keylen(ni->ni_rsncipher);
        BE_WRITE_2(key->keylen, keylen);
    } else
        info |= EAPOL_KEY_SECURE;
    
    /* write the key info field */
    BE_WRITE_2(key->info, info);
    
    /* empty key data field */
    mbuf_pkthdr_setlen(m, sizeof(*key));
    mbuf_setlen(m, sizeof(*key));
    
    if (ic->ic_if.if_flags & IFF_DEBUG)
        XYLog("%s: sending msg %d/%d of the %s handshake to %s\n",
               ic->ic_if.if_xname, 4, 4, "4-way",
               ether_sprintf(ni->ni_macaddr));
    
    return ieee80211_send_eapol_key(ic, m, ni, &ni->ni_ptk);
}

#ifndef IEEE80211_STA_ONLY
/*
 * Send Group Key Handshake Message 1 to the supplicant.
 */
int
ieee80211_send_group_msg1(struct ieee80211com *ic, struct ieee80211_node *ni)
{
    XYLog("%s Send Group Key Handshake Message 1 to the supplicant.\n", __FUNCTION__);
    struct ieee80211_eapol_key *key;
    const struct ieee80211_key *k;
    mbuf_t m;
    u_int16_t info;
    u_int8_t *frm;
    u_int8_t kid;
    
    ni->ni_rsn_gstate = RSNA_REKEYNEGOTIATING;
    if (++ni->ni_rsn_retries > 3) {
        IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_DEAUTH,
                            IEEE80211_REASON_GROUP_TIMEOUT);
        ieee80211_node_leave(ic, ni);
        return 0;
    }
    if (ni->ni_flags & IEEE80211_NODE_REKEY)
        kid = (ic->ic_def_txkey == 1) ? 2 : 1;
    else
        kid = ic->ic_def_txkey;
    k = &ic->ic_nw_keys[kid];
    
    m = ieee80211_get_eapol_key(MBUF_DONTWAIT, MT_DATA,
                                ((ni->ni_rsnprotos == IEEE80211_PROTO_WPA) ?
                                 k->k_len : 2 + 6 + k->k_len) +
                                ((ni->ni_flags & IEEE80211_NODE_MFP) ? 2 + 28 : 0) +
                                15);
    if (m == NULL)
        return ENOMEM;
    key = mtod(m, struct ieee80211_eapol_key *);
    memset(key, 0, sizeof(*key));
    
    info = EAPOL_KEY_KEYACK | EAPOL_KEY_KEYMIC | EAPOL_KEY_SECURE |
    EAPOL_KEY_ENCRYPTED;
    
    ni->ni_replaycnt++;
    BE_WRITE_8(key->replaycnt, ni->ni_replaycnt);
    
    frm = (u_int8_t *)&key[1];
    if (ni->ni_rsnprotos == IEEE80211_PROTO_WPA) {
        /* WPA does not have GTK KDE */
        BE_WRITE_2(key->keylen, k->k_len);
        memcpy(frm, k->k_key, k->k_len);
        frm += k->k_len;
        info |= (k->k_id & 0x3) << EAPOL_KEY_WPA_KID_SHIFT;
        if (ni->ni_rsncipher == IEEE80211_CIPHER_USEGROUP)
            info |= EAPOL_KEY_WPA_TX;
    } else {	/* RSN */
        frm = ieee80211_add_gtk_kde(frm, ni, k);
        if (ni->ni_flags & IEEE80211_NODE_MFP) {
            if (ni->ni_flags & IEEE80211_NODE_REKEY)
                kid = (ic->ic_igtk_kid == 4) ? 5 : 4;
            else
                kid = ic->ic_igtk_kid;
            frm = ieee80211_add_igtk_kde(frm,
                                         &ic->ic_nw_keys[kid]);
        }
    }
    /* RSC = last transmit sequence number for the GTK */
    LE_WRITE_6(key->rsc, k->k_tsc);
    
    /* write the key info field */
    BE_WRITE_2(key->info, info);
    
    size_t l = frm - (u_int8_t *)key;
    mbuf_pkthdr_setlen(m, l);
    mbuf_setlen(m, l);
    
    if (ic->ic_if.if_flags & IFF_DEBUG)
        XYLog("%s: sending msg %d/%d of the %s handshake to %s\n",
               ic->ic_if.if_xname, 1, 2, "group key",
               ether_sprintf(ni->ni_macaddr));
    
    return ieee80211_send_eapol_key(ic, m, ni, &ni->ni_ptk);
}
#endif	/* IEEE80211_STA_ONLY */

/*
 * Send Group Key Handshake Message 2 to the authenticator.
 */
int
ieee80211_send_group_msg2(struct ieee80211com *ic, struct ieee80211_node *ni,
                          const struct ieee80211_key *k)
{
    XYLog("%s\n", __FUNCTION__);
    struct ieee80211_eapol_key *key;
    u_int16_t info;
    mbuf_t m;
    
    m = ieee80211_get_eapol_key(MBUF_DONTWAIT, MT_DATA, 0);
    if (m == NULL)
        return ENOMEM;
    key = mtod(m, struct ieee80211_eapol_key *);
    memset(key, 0, sizeof(*key));
    
    info = EAPOL_KEY_KEYMIC | EAPOL_KEY_SECURE;
    
    /* copy key replay counter from authenticator */
    BE_WRITE_8(key->replaycnt, ni->ni_replaycnt);
    
    if (ni->ni_rsnprotos == IEEE80211_PROTO_WPA) {
        /* WPA sets the key length and key id fields here */
        BE_WRITE_2(key->keylen, k->k_len);
        info |= (k->k_id & 3) << EAPOL_KEY_WPA_KID_SHIFT;
    }
    
    /* write the key info field */
    BE_WRITE_2(key->info, info);
    
    /* empty key data field */
    mbuf_pkthdr_setlen(m, sizeof(*key));
    mbuf_setlen(m, sizeof(*key));
    
    if (ic->ic_if.if_flags & IFF_DEBUG)
        XYLog("%s: sending msg %d/%d of the %s handshake to %s\n",
               ic->ic_if.if_xname, 2, 2, "group key",
               ether_sprintf(ni->ni_macaddr));
    
    return ieee80211_send_eapol_key(ic, m, ni, &ni->ni_ptk);
}

/*
 * EAPOL-Key Request frames are sent by the supplicant to request that the
 * authenticator initiates either a 4-Way Handshake or Group Key Handshake,
 * or to report a MIC failure in a TKIP MSDU.
 */
int
ieee80211_send_eapol_key_req(struct ieee80211com *ic,
                             struct ieee80211_node *ni, u_int16_t info, u_int64_t tsc)
{
    XYLog("%s\n", __FUNCTION__);
    struct ieee80211_eapol_key *key;
    mbuf_t m;
    
    m = ieee80211_get_eapol_key(MBUF_DONTWAIT, MT_DATA, 0);
    if (m == NULL)
        return ENOMEM;
    key = mtod(m, struct ieee80211_eapol_key *);
    memset(key, 0, sizeof(*key));
    
    info |= EAPOL_KEY_REQUEST;
    BE_WRITE_2(key->info, info);
    
    /* in case of TKIP MIC failure, fill the RSC field */
    if (info & EAPOL_KEY_ERROR)
        LE_WRITE_6(key->rsc, tsc);
    
    /* use our separate key replay counter for key requests */
    BE_WRITE_8(key->replaycnt, ni->ni_reqreplaycnt);
    ni->ni_reqreplaycnt++;
    
    /* empty key data field */
    mbuf_pkthdr_setlen(m, sizeof(*key));
    mbuf_setlen(m, sizeof(*key));
    
    if (ic->ic_if.if_flags & IFF_DEBUG)
        XYLog("%s: sending EAPOL-Key request to %s\n",
               ic->ic_if.if_xname, ether_sprintf(ni->ni_macaddr));
    
    return ieee80211_send_eapol_key(ic, m, ni, &ni->ni_ptk);
}
